package com.touchapk.opencv;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.opencv.calib3d.Calib3d;
import org.opencv.core.Core;
import org.opencv.core.CvException;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.features2d.DMatch;
import org.opencv.features2d.KeyPoint;
import org.opencv.imgproc.Imgproc;

/**
 * @description
 * @author yangkeyun
 * @email coyun@qq.com
 * @date 2016年7月18日
 * @version V1.0
 */
public class Finder {
	
	public Finder(){
		 System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	
	/**
	 * 
	 * @author yangkeyun
	 * @description
	 * @param fontHeight
	 *            字体大小 默认 30
	 * @param drawStr
	 *            生产图片的文字，多行数组
	 * @return
	 */
	private BufferedImage drawTranslucentStringPic(Integer fontHeight,String[] drawStr) {

		try {
			int width = 0;
			for (String s : drawStr) {

				int maxWidth = 0;

				for (int i = 0; i < s.length(); i++) {

					int c = s.charAt(i);

					if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)) {
						maxWidth += fontHeight * 0.56;
					} else if ((c >= 48 && c <= 57)) {
						maxWidth += fontHeight * 0.6;
					} else if ((c == 32)) {
						maxWidth += fontHeight * 0.3;
					} else {
						maxWidth += fontHeight;
					}
				}
				if (maxWidth > width) {
					width = maxWidth;
				}
			}

			int height = fontHeight + (fontHeight * 2 / 10);
			height *= drawStr.length;

			BufferedImage buffImg = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);

			Graphics2D gd = buffImg.createGraphics();

			gd.setFont(new Font("微软雅黑", Font.PLAIN, fontHeight)); // 设置字体

			gd.setColor(Color.WHITE); // 设置颜色

			int y = fontHeight;

			for (String s : drawStr) {
				gd.drawString(s, 0, y);
				y += (fontHeight + (fontHeight * 2 / 10));
			}
			return buffImg;
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 
	 * @author yangkeyun
	 * @description 文本获取坐标
	 * @param width
	 *            图片宽
	 * @param height
	 *            图片高
	 * @param data
	 *            图片数据
	 * @param texts
	 * @param fontHeight
	 *            文本字体大小
	 * @return
	 */
	public int[] findText(byte[] data, String[] texts, Integer fontHeight,double degree) {

		
		
		if(data==null||(data!=null&&data.length==0)){
			return new int[] { -1, -1 };
		}
		
		Mat scene;
		
		try {
			scene = DetectUtility.img2Mat(ImageIO.read(new ByteArrayInputStream(data)));
		} catch (Exception e) {
			return new int[] { -1, -1 };
		}
	
		//Highgui.imwrite("aabbcc.png", scene);
		
		Imgproc.cvtColor(scene, scene, Imgproc.COLOR_RGB2GRAY);
		
		Mat sceneDesc = new Mat();
		MatOfKeyPoint sceneMkp = new MatOfKeyPoint();
		DetectUtility.analyze(scene, sceneMkp, sceneDesc);

		Mat target = DetectUtility.img2Mat(drawTranslucentStringPic(fontHeight, texts));
		
		Mat targetDesc = new Mat();
		MatOfKeyPoint targetMkp = new MatOfKeyPoint();

		DetectUtility.analyze(target, targetMkp, targetDesc);

		MatOfDMatch masks = new MatOfDMatch();
		DetectUtility.match(sceneDesc, targetDesc,masks);
		
		KeyPoint[] qkeyPoints = sceneMkp.toArray();
		KeyPoint[] tkeyPoints = targetMkp.toArray();

		double max_dist = 0;
		double min_dist = 9999;

		List<DMatch> mList = masks.toList();

		for (int i = 0; i < mList.size(); i++) {
			double dist = mList.get(i).distance;
			if (dist < min_dist)
				min_dist = dist;
			if (dist > max_dist)
				max_dist = dist;
		}

		List<DMatch> goodMatches = new ArrayList<DMatch>();

		for (int i = 0; i < mList.size(); i++) {

			float distance = mList.get(i).distance;

			if (distance < degree * min_dist) {
				goodMatches.add(mList.get(i));
			}
		}

		Point[] qPoints = new Point[goodMatches.size()];
		Point[] tPoints = new Point[goodMatches.size()];

		for (int i = 0; i < goodMatches.size(); i++) {
			qPoints[i] = qkeyPoints[goodMatches.get(i).queryIdx].pt;
			tPoints[i] = tkeyPoints[goodMatches.get(i).trainIdx].pt;
		}

		if (tPoints.length < 4 || qPoints.length < 4) {
			return new int[] { -1, -1 };
		}

		MatOfPoint2f obj = new MatOfPoint2f();
		MatOfPoint2f scenes = new MatOfPoint2f();

		obj.fromArray(tPoints);
		scenes.fromArray(qPoints);

		Mat mask = new Mat();
		Mat H = Calib3d.findHomography(obj, scenes, Calib3d.RANSAC, 0.995, mask);
		Mat objCorners = new Mat(4, 1, CvType.CV_32FC2);
		Mat sceneCorners = new Mat(4, 1, CvType.CV_32FC2);

		objCorners.put(0, 0, new double[] { 0, 0 });
		objCorners.put(1, 0, new double[] { scene.cols(), 0 });
		objCorners.put(2, 0, new double[] { scene.cols(), scene.rows() });
		objCorners.put(3, 0, new double[] { 0, scene.rows() });

		try {
			Core.perspectiveTransform(objCorners, sceneCorners, H);
		} catch (CvException e) {
			e.printStackTrace();
			return new int[] { -1, -1 };
		}

		for (int i = 0; i < 3; i++) {
			for (int k = 0; k < 2; k++) {
				if (sceneCorners.get(i, 0)[k] <= 0) {
					return new int[] { -1, -1 };
				}
			}
		}

		double[] xs = sceneCorners.get(0, 0);

		Point matchLoc = new Point(xs[0], xs[1]);

		Point targetPoint = qPoints[0];

		double mindst = Math.sqrt((targetPoint.x - matchLoc.x)
				* (targetPoint.x - matchLoc.x) + (targetPoint.y - matchLoc.y)
				* (targetPoint.y - matchLoc.y));

		for (int i = 0; i < qPoints.length; i++) {
			Point p = qPoints[i];
			double md = Math.sqrt((p.x - matchLoc.x) * (p.x - matchLoc.x)
					+ (p.y - matchLoc.y) * (p.y - matchLoc.y));
			if (md < mindst) {
				mindst = md;
				targetPoint = p;
			}
		}

		
		return new int[] { (int) Math.rint(targetPoint.x),(int) Math.rint(targetPoint.y) };
	}
	
	
	/**
	 * 
	* @author yangkeyun 
	* @description 获取指定坐标的颜色值
	* @param data
	* @param x
	* @param y
	* @return
	 */
	public int[] getColor(byte []data,int x,int y){
		
		if(data==null||(data!=null&&data.length==0)){
			return new int[]{-1,-1,-1};
		}
		
		Mat scene;
		
		try {
			scene = DetectUtility.img2Mat(ImageIO.read(new ByteArrayInputStream(data)));
		} catch (Exception e) {
			return new int[]{-1,-1,-1};
		}
		
		 	Mat mHsvMat= scene;
	        
			mHsvMat.convertTo(mHsvMat, CvType.CV_16SC3);
			int imgWidth = mHsvMat.cols();// 为img宽
			int imgHeight = mHsvMat.rows();// 为img高
			short imgPixel[] = new short[imgWidth * imgHeight * 3];// 申请一位数组
			mHsvMat.get(0, 0, imgPixel);// 把像素值存入一维数组中
			
			short b = imgPixel[(int) ((imgWidth * y + x) * 3)];
			short g = imgPixel[(int) ((imgWidth * y + x) * 3 + 1)];
			short r = imgPixel[(int) ((imgWidth * y + x) * 3 + 2)];
			
			return new int[]{b,g,r};
	}
	
	
	/**
	 * 
	* @author yangkeyun 
	* @description 获取指定颜色的坐标集
	* @param data
	* @param colorBGR
	* @param degreeStart
	* @param degreeEnd
	* @return
	 */
	public List<int[]> findColor(byte[] data,String colorBGR,double degreeStart,double degreeEnd){
		
		List<int[]> target =new ArrayList<int[]>();
		
		if(data==null||(data!=null&&data.length==0)){
			return target;
		}
		
		int color[]=DetectUtility.c16To10(colorBGR);
		
		if(color[0]==-1&&color[1]==-1&&color[2]==-1){
			return target;
		}
		

		Mat scene;
		
		try {
			scene = DetectUtility.img2Mat(ImageIO.read(new ByteArrayInputStream(data)));
			
		} catch (Exception e) {
			return target;
		}
		
		Mat touchedRegionRgba = new Mat(1, 1, CvType.CV_8UC3,new Scalar( color[0], color[1], color[2], 255.0)).submat(new Rect(0, 0, 1, 1));

		Mat touchedRegionHsv = new Mat();

		Imgproc.cvtColor(touchedRegionRgba, touchedRegionHsv,Imgproc.COLOR_BGR2HLS_FULL);

		Scalar mBlobColorHsv = Core.sumElems(touchedRegionHsv);

		ColorBlobDetector mDetector = new ColorBlobDetector();
		  
		mDetector.setHsvColor(mBlobColorHsv);
	
		mDetector.process(scene);
		 
        List<MatOfPoint> contours = mDetector.getContours();
     
        Mat mHsvMat= scene.clone();
        
        
		mHsvMat.convertTo(mHsvMat, CvType.CV_16SC3);
		int imgWidth = mHsvMat.cols();// 为img宽
		int imgHeight = mHsvMat.rows();// 为img高
		
		short imgPixel[] = new short[imgWidth * imgHeight * 3];// 申请一位数组
		mHsvMat.get(0, 0, imgPixel);// 把像素值存入一维数组中
		
		if(degreeStart>100)degreeStart=100;//匹配度大于100的时候就是100
		if(degreeStart<0)degreeStart=0;//小于0的时候就是0
		
		short bx = (short) color[0], gy = (short) color[1], rz = (short) color[2];//目标颜色
		
		for (MatOfPoint p : contours) {

			int x = 0;
			int y = 0;
			int index = 0;
			
			Point[] pl = p.toArray();
			double minX = pl[0].x;
			double maxX = minX;
			double minY = pl[0].y;
			double maxY = minY;

			for (Point pp : pl) {
				
				x += pp.x;
				y += pp.y;
				++index;
				
				if (pp.x < minX)
					minX = pp.x;
				if (pp.x > maxX)
					maxX = pp.x;
				if (pp.y < minY)
					minY = pp.y;
				if (pp.y > maxY)
					maxY = pp.y;
			}

			double minDist = 441.67;//范围
			
			for (double i = minX; i < maxX; i++) {
				
				for (double j = minY; j < maxY; j++) {
					
					short b = imgPixel[(int) ((imgWidth * j + i) * 3)];
					short g = imgPixel[(int) ((imgWidth * j + i) * 3 + 1)];
					short r = imgPixel[(int) ((imgWidth * j + i) * 3 + 2)];

					double distance = Math.sqrt((b - bx) * (b - bx) + (g - gy)* (g - gy) + (r - rz) * (r - rz));

					if (distance < minDist) {
						minDist = distance;
					}
				}
			}	
			
			if (degreeEnd <= 0 || degreeEnd < degreeStart) {
				if (minDist <= (degreeStart * 4.41)) {
					target.add(new int[] { x/index, y/index });
					
				}
			} else {
				degreeEnd = degreeEnd > 100 ? 100 : degreeEnd;
				if (minDist > (degreeStart * 4.41)&& minDist <= (degreeEnd * 4.41)) {
					target.add(new int[] { x/index, y/index });
					
				}
			}
		}
		
		return target;
	}
	
	/**
	 * 
	* @author yangkeyun 
	* @description  根据参照颜色查找，参照的颜色必须存在
	* @param data
	* @param colorBGR
	* @param degreeStart
	* @param degreeEnd
	* @param subColorBGR
	* @param direction  up 1,right 2,down 3,left 4
	* @return
	 */
	
	public List<int[]> findColor(byte[] data,String colorBGR,double degreeStart,double degreeEnd,String subColorBGR,int direction){
		
		List<int[]> targetList=findColor(data,colorBGR,degreeStart,degreeEnd);
		
		List<int[]> subList=findColor(data,subColorBGR,0,0);
		
		List<int[]> resultList=new ArrayList<int[]>();
		
		String exit="";
	
		for(int[] t:targetList){
			
			for(int[] s:subList){
				
				String e = t[0] + "-" + t[1];

				if (!exit.contains(e)) {
					exit += "#" + e;
					if (direction == 1 && t[1] < s[1]) {
						resultList.add(t);
					} else if (direction == 3 && t[1] > s[1]) {
						resultList.add(t);
					} else if (direction == 2 && t[0] > s[0]) {
						resultList.add(t);
					} else if (direction == 4 && t[0] < s[0]) {
						resultList.add(t);
					}
				}
			}
			
		}
		
		
		
		return resultList;
	}
	
	
	/**
	 * 
	* @author yangkeyun 
	* @description 多点颜色定位
	* @param data
	* @param colorBGR
	* @param degreeStart
	* @param degreeEnd
	* @param subColorBGR
	* @param direction
	* @param subColorBGR1
	* @param direction1
	* @return
	 */
	public List<int[]> findColor(byte[] data,String colorBGR,double degreeStart,double degreeEnd,String subColorBGR,int direction,String subColorBGR1,int direction1){
	
		List<int[]> targetList=findColor(data,colorBGR,degreeStart,degreeEnd);
		
		List<int[]> subList=findColor(data,subColorBGR,0,0);
		
		List<int[]> subList1=findColor(data,subColorBGR1,0,0);
		
		List<int[]> resultList=new ArrayList<int[]>();
		
		String exit="";
		
		for (int[] t : targetList) {

			for (int[] s : subList) {

				for (int[] s1 : subList1) {
					
					String e = t[0] + "-" + t[1];

					if (!exit.contains(e)) {
						exit += "#" + e;
						if ((direction == 1 && t[1] < s[1])
								&& (direction1 == 1 && t[1] < s1[1])) {
							resultList.add(t);
						} else if ((direction == 1 && t[1] < s[1])
								&& (direction1 == 2 && t[0] > s1[0])) {
							resultList.add(t);
						} else if ((direction == 1 && t[1] < s[1])
								&& (direction1 == 3 && t[1] > s1[1])) {
							resultList.add(t);
						} else if ((direction == 1 && t[1] < s[1])
								&& (direction1 == 4 && t[0] < s1[0])) {
							resultList.add(t);
						} else if ((direction == 3 && t[1] > s[1])
								&& (direction1 == 1 && t[1] < s1[1])) {
							resultList.add(t);
						} else if ((direction == 3 && t[1] > s[1])
								&& (direction1 == 2 && t[0] > s1[0])) {
							resultList.add(t);
						} else if ((direction == 3 && t[1] > s[1])
								&& (direction1 == 3 && t[1] > s1[1])) {
							resultList.add(t);
						} else if ((direction == 3 && t[1] > s[1])
								&& (direction1 == 4 && t[0] < s1[0])) {
							resultList.add(t);
						} else if ((direction == 2 && t[0] > s[0])
								&& (direction1 == 1 && t[1] < s1[1])) {
							resultList.add(t);
						} else if ((direction == 2 && t[0] > s[0])
								&& (direction1 == 2 && t[0] > s1[0])) {
							resultList.add(t);
						} else if ((direction == 2 && t[0] > s[0])
								&& (direction1 == 3 && t[1] > s1[1])) {
							resultList.add(t);
						} else if ((direction == 2 && t[0] > s[0])
								&& (direction1 == 4 && t[0] < s1[0])) {
							resultList.add(t);
						} else if ((direction == 4 && t[0] < s[0])
								&& (direction1 == 1 && t[1] < s1[1])) {
							resultList.add(t);
						} else if ((direction == 4 && t[0] < s[0])
								&& (direction1 == 2 && t[0] > s1[0])) {
							resultList.add(t);
						} else if ((direction == 4 && t[0] < s[0])
								&& (direction1 == 3 && t[1] > s1[1])) {
							resultList.add(t);
						} else if ((direction == 4 && t[0] < s[0])
								&& (direction1 == 4 && t[0] < s1[0])) {
							resultList.add(t);
						}
					}
				}
			}
		}
		
		return resultList;
	}
	

}
